//
// Created by benxb on 2021/11/21.
//

#ifndef BENXDB_DATA_CHUNK_H
#define BENXDB_DATA_CHUNK_H

#include <cstdio>
#include <cstring>
#include <vector>
#include "common/config.h"
#include "common/logger.h"
#include "type/value.h"

namespace benxdb {

class DataChunk {
public:
    explicit DataChunk() {
        // ResetMemory();
        next_slot_ = 0;
        chunk_size_ = 0;
        data_type_ = INVALID;
        record_count_ = 0;
        capacity_ = Value(TypeId::DECIMAL).GetLength() * RECORD_COUNT_PER_CHUNK;
    };

    explicit DataChunk(TypeId data_type) {
        // ResetMemory();
        data_type_ = data_type;
        next_slot_ = 0;
        chunk_size_ = 0;
        record_count_ = 0;
        int len = Type::GetTypeSize(data_type);
        capacity_ = len * RECORD_COUNT_PER_CHUNK;
    };

    void Clear();

    /** Default destructor */
    ~DataChunk() = default;

    void SetDataType(TypeId data_type) {
        data_type_ = data_type;
    }

    void SetChunkSize(int32_t size) {
        chunk_size_ = size;
    }

    bool IsFull() const {
//        return chunk_size_ == CHUNK_SIZE;
        return record_count_ == RECORD_COUNT_PER_CHUNK or chunk_size_ == CHUNK_SIZE;
    }

    TypeId GetDataType() {
        return data_type_;
    }

    char* GetData() {
        return data_;
    }

    int32_t GetChunkCapacity() {
        return CHUNK_SIZE;
    }

    int32_t GetChunkSize() {
        return chunk_size_;
    }

    int32_t GetValueSize() {
        return Type::GetTypeSize(data_type_);
    }

    void GetMaxMinValue(Value& max_val, Value& min_val);

    chunk_id_t GetChunkId() const {
        return chunk_id_;
    }

    void SetChunkId(chunk_id_t chunk_id) {
        chunk_id_ = chunk_id;
    }

    /**
     * Append new record into this chunk
     * @param value
     * @return
     */
    bool AppendRecord(const Value& value);

    inline bool AppendData(const char* data, int size) {
        if (chunk_size_ + size > capacity_) {
            LOG_ERROR("Chunk size out of bound.");
            return false;
        }

        memcpy(data_+next_slot_, data, size);
        chunk_size_ += size;
        next_slot_ += size;
        record_count_++;
        return true;
    };

    /**
     * Append many records into this chunk
     * @param records
     * @return
     */
    bool AppendManyRecords(const std::vector<Value>& records);

    int32_t QueryEqual(Value value);

    Value QueryIndex(int32_t index);

    std::vector<Value> QueryRange(const Value& min_val, const Value& max_val);

    std::vector<std::pair<int32_t, Value>> QueryIndexValueRange(Value min_val, Value max_val);

    std::vector<Value> QueryRangeIndex(int32_t begin_index, int32_t end_index);

protected:
    static_assert(sizeof(page_id_t) == 4);
    static constexpr size_t OFFSET_BLOCK_INIT = 0;

private:
    inline void ResetMemory() {
        memset(data_, OFFSET_BLOCK_INIT, CHUNK_SIZE);
    }

    /** The actual data_ array */
    char data_[CHUNK_SIZE]{};
    /** Id of this chunk */
    chunk_id_t chunk_id_ = INVALID_CHUNK_ID;

    int32_t chunk_size_;

    int32_t capacity_;

    // for fixed length types
    int32_t record_count_;

    // next insert slot
    int32_t next_slot_;

    TypeId data_type_;

    template <typename T>
    void GetMMValue(Value &max_val, Value &min_val);
};


}


#endif //BENXDB_DATA_CHUNK_H
